package com.ghostbusters.fingertrack.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.Border;

import com.ghostbusters.fingertrack.calibration.Calibration;
import com.ghostbusters.fingertrack.control.ApplicationControl;
import com.ghostbusters.fingertrack.control.CalibrationControl;
import com.ghostbusters.fingertrack.control.MatchControl;
import com.ghostbusters.fingertrack.wii.IRPoint;
import com.ghostbusters.fingertrack.wii.WiiMain;
import com.ghostbusters.fingertrack.wii.WiiSubscriber;
import com.ghostbusters.fingertracker.util.Serializer;

public class MainFrame extends JFrame implements ActionListener, WiiSubscriber, Runnable {
	public final static Border etchedBorder = BorderFactory
			.createEtchedBorder();
	public final static int XDIM = 900;
	public final static int YDIM = 600;

	public boolean calibrationMode = true;

	private JTextArea messageBox;
	private JTextArea matchesBox;
	private JLabel statusBar;
	private JPanel calibrationPanel = getCalibratePanel();
	private JPanel matchPanel = getMatchPanel();
	private JPanel buttonPane;
	private LugiaPanel lugia;
	private DrawPanel drawPanel;
	private JButton cStart;// calibration start
	private JButton cStop;// calibration stop
	private JButton mStart;// match start
	private JButton mStop;// match stop

	// calibration and matching
	// private Calibration<Character> calibration = new
	// Calibration<Character>();
	Calibration<Character> calibration = Serializer.deserialize(
			new Calibration<Character>(), "bin/calibration.txt");
	private CalibrationControl<Character> calibrationControl = new CalibrationControl<Character>(
			calibration);
	private ApplicationControl<Character> applicationControl = new ApplicationControl(
			calibration);
	private List<Character> characters = new ArrayList<Character>();
	Character currentSymbol = 'a';
	private int characterCounter = 0;
	// applicationcontrol
	Calibration<Character> calibrationapp = new Calibration<Character>();
	ApplicationControl control = new ApplicationControl(calibrationapp);

	// private MatchCallBack matchCallBack = new MatchCallBack();
	private MatchControl<Character> matchControl = new MatchControl<Character>(
			this, calibration);

	// CalibrationControl<Character> calibrationControl = new
	// CalibrationControl<Character>();
	// ArrayList<Calibration<Character>> calibrations = new
	// ArrayList<Calibration<Character>>(0);

	public static void main(String[] args) {
		setNativeLAF("Nimbus");
		MainFrame m = new MainFrame("Hi");
		java.util.Scanner s = new java.util.Scanner(System.in);

		List<IRPoint> p = new ArrayList<IRPoint>(0);
		for (int i = 0; i < DrawPanel.pointsTracked; i++) {
			p.add(new IRPoint((double) i / DrawPanel.pointsTracked, (double) i
					/ DrawPanel.pointsTracked));
		}
		m.newPoints(p);

		while (true) {
			System.out.print("Enter text: ");
			String cheese = s.nextLine();
			if (cheese.equals("")) {
				p = new ArrayList<IRPoint>(0);
				p.add(new IRPoint(0, 0));
				m.newPoints(p);
				continue;
			}
			// m.println(cheese);
			String[] points = cheese.split(",");

			p = new ArrayList<IRPoint>(0);
			p.add(0,
					new IRPoint(Double.parseDouble(points[0]), Double
							.parseDouble(points[1])));
			m.newPoints(p);
		}
	}

	public static MainFrame getMainFrame(String s){
		MainFrame m = new MainFrame(s);
		Thread t = new Thread(m, "Main Frame");
		t.start();
		return m;
	}
	
	public MainFrame(String title) {
		super(title);
		WiiMain.subscribe(this);
		init();
		println("Welcome!");
	}

	public void printMatches(String match) {
		StringBuilder sb = new StringBuilder();
		if (matchesBox.getText() != null)
			sb.append(matchesBox.getText());
		else
			sb.append("");
		if (match != null)
			matchesBox.setText(sb.append(match).toString());
	}

	public void println(Object... o) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < o.length; i++) {
			sb.append(o[i].toString());
		}
		sb.append('\n');
		messageBox.append(sb.toString());
	}

	@Override
	public void newPoints(List<IRPoint> p) {
		for (int i = 0; i < p.size(); i++) {
			drawPanel.newPoint(p.get(i));
		}

	}
	
	public void run(){
		while(true){
			
			drawPanel.repaint();
			repaint();
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void init() {
		initializeCalibrationChars();

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setPreferredSize(new Dimension(XDIM, YDIM));

		JPanel rootPane = new JPanel();
		rootPane.setPreferredSize(new Dimension(XDIM, YDIM));
		rootPane.setLayout(new BoxLayout(rootPane, BoxLayout.X_AXIS));
		rootPane.setBorder(etchedBorder);
		JPanel InterfacePanel = getInterfacePanel();
		JPanel SidePanel = getSidePanel();
		rootPane.add(InterfacePanel);
		rootPane.add(SidePanel);

		JMenuBar menuBar = getJMenuBar();
		this.setJMenuBar(menuBar);

		setContentPane(rootPane);
		pack();
		setVisible(true);
		// applicationControl.startApplication();
	}

	private void initializeCalibrationChars() {
		for (int i = 0; i < 26; i++) {
			characters.add((char) ('a' + i));
		}

	}

	public JMenuBar getJMenuBar() {
		JMenuBar menu = new JMenuBar();
		JMenu blah = new JMenu("Application");
		JMenuItem turnOn = new JMenuItem("Start application");
		turnOn.setActionCommand("on_application");
		turnOn.addActionListener(this);
		blah.add(turnOn);
		JMenuItem turnOff = new JMenuItem("Stop application");
		turnOff.setActionCommand("off_application");
		turnOff.addActionListener(this);
		blah.add(turnOff);
		menu.add(blah);

		return menu;
	}

	public JPanel getInterfacePanel() {
		JPanel pane = new JPanel();
		pane.setPreferredSize(new Dimension(3 * XDIM / 4, YDIM));
		pane.setBorder(etchedBorder);
		pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
		drawPanel = new DrawPanel();
		drawPanel.setPreferredSize(new Dimension(2 * XDIM / 3, 2 * YDIM / 3));
		drawPanel.setBorder(etchedBorder);
		pane.add(drawPanel);

		JPanel messagepane = new JPanel(new GridBagLayout());
		messageBox = new JTextArea(8, 30);
		messageBox.setEditable(false);
		JScrollPane scrollPane = new JScrollPane(messageBox);
		GridBagConstraints c = new GridBagConstraints();
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.BOTH;
		c.weightx = 1.0;
		c.weighty = 1.0;

		messagepane.add(scrollPane, c);
		pane.add(messagepane);

		buttonPane = new JPanel();
		buttonPane.setPreferredSize(new Dimension(2 * XDIM / 3, 3 * YDIM / 24));
		buttonPane.setBorder(etchedBorder);
		if (calibrationMode) {
			buttonPane.add(calibrationPanel);
		} else {
			buttonPane.add(matchPanel);
		}
		pane.add(buttonPane);

		JPanel tickerLabelPane = new JPanel(new BorderLayout());
		String cheese = "Welcome! ";
		if (calibrationMode) {
			cheese = cheese + "Calibration mode - idle";
		} else {
			cheese = cheese + "Matching mode - idle";
		}
		statusBar = new JLabel(cheese);
		statusBar.setPreferredSize(new Dimension(2 * XDIM / 3, YDIM / 24));
		tickerLabelPane.add(statusBar);
		pane.add(tickerLabelPane);

		return pane;
	}

	public JPanel getSidePanel() {
		JPanel pane = new JPanel();
		pane.setOpaque(false);
		pane.setPreferredSize(new Dimension(XDIM / 4, YDIM));
		pane.setBorder(etchedBorder);

		JPanel messagepane = new JPanel(new GridBagLayout());
		messagepane.setPreferredSize(new Dimension(XDIM / 4, YDIM / 2));
		matchesBox = new JTextArea(60, 20);
		matchesBox.setEditable(false);
		JScrollPane scrollPane = new JScrollPane(matchesBox);
		GridBagConstraints c = new GridBagConstraints();
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.BOTH;
		c.weightx = 1.0;
		c.weighty = 1.0;
		messagepane.add(scrollPane, c);
		pane.add(messagepane);

		return pane;
	}

	public JPanel getCalibratePanel() {
		JPanel pane = new JPanel();
		pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
		JButton matchButton = new JButton("Match");
		matchButton.setActionCommand("match");
		matchButton.addActionListener(this);
		pane.add(matchButton);
		cStart = new JButton("Start");
		cStart.setActionCommand("start_calib");
		cStart.addActionListener(this);
		pane.add(cStart);
		cStop = new JButton("Stop");
		cStop.setActionCommand("stop_calib");
		cStop.addActionListener(this);
		cStop.setEnabled(false);
		pane.add(cStop);
		return pane;
	}

	public JPanel getMatchPanel() {
		JPanel pane = new JPanel();
		pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
		JButton calibButton = new JButton("Calib");
		calibButton.setActionCommand("calibrate");
		calibButton.addActionListener(this);
		pane.add(calibButton);
		mStart = new JButton("Start");
		mStart.setActionCommand("start_match");
		mStart.addActionListener(this);
		pane.add(mStart);
		mStop = new JButton("Stop");
		mStop.setActionCommand("stop_match");
		mStop.addActionListener(this);
		mStop.setEnabled(false);
		pane.add(mStop);
		return pane;
	}

	public void actionPerformed(ActionEvent e) {
		if ("match".equals(e.getActionCommand())) {
			calibrationMode = false;
			buttonPane.removeAll();
			buttonPane.add(matchPanel);
			mStart.setEnabled(true);
			mStop.setEnabled(false);

			statusBar.setText("Matching mode - idle");

			pack();
			repaint();
		} else if ("calibrate".equals(e.getActionCommand())) {
			calibrationMode = true;
			buttonPane.removeAll();
			buttonPane.add(calibrationPanel);
			cStart.setEnabled(true);
			cStop.setEnabled(false);

			statusBar.setText("Calibration mode - idle");

			pack();
			repaint();
		} else if ("start_calib".equals(e.getActionCommand())) {
			cStart.setEnabled(false);
			cStop.setEnabled(true);

			statusBar.setText("Calibration mode - calibrating character "
					+ characters.get(characterCounter));
			calibrationControl.startCalibratingSymbol(characters
					.get(characterCounter));
			// start calibration
			/*
			 * 
			 * Calibration<Character> c = null;
			 * calibrationControl.startCalibratingSymbol(currentSymbol,c);
			 */

		} else if ("stop_calib".equals(e.getActionCommand())) {
			cStart.setEnabled(true);
			cStop.setEnabled(false);

			statusBar.setText("Calibration mode - idle");

			calibrationControl.endCalibratingSymbol(characters
					.get(characterCounter));

			characterCounter++;
			Serializer.serialize((Serializable) calibration,
					"bin/calibration.txt");
			println("Next letter to calibrate: "
					+ characters.get(characterCounter));
			// end calibration
			/*
			 * calibrationControl.endCalibratingSymbol(currentSymbol);
			 * calibrations.add(c);
			 */
		} else if ("start_match".equals(e.getActionCommand())) {
			mStart.setEnabled(false);
			mStop.setEnabled(true);

			statusBar.setText("Matching mode - working...");
			matchControl.startMatch();

		} else if ("stop_match".equals(e.getActionCommand())) {
			mStart.setEnabled(true);
			mStop.setEnabled(false);

			statusBar.setText("Matching mode - idle");

			try {
				matchControl.endMatch();
			} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		} else if ("on_application".equals(e.getActionCommand())) {
			println("Application ON");
			control.startApplication();

		} else if ("off_application".equals(e.getActionCommand())) {
			println("Application OFF");
			control.endApplication();
		}
	}

	public static void setNativeLAF(String LAFname) {
		try {
			for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
				if (LAFname.equals(info.getName())) {
					UIManager.setLookAndFeel(info.getClassName());
					break;
				}
			}
		} catch (Exception e) {
			System.err.println(LAFname
					+ " Look and Feel not found. Using metal instead.");
		}
	}

}